"""
@File    :  system_log_server_layout.py
@Author  :  LeeSen
@Date    :  2024-06-27
@Version :  Python 3.7.4
"""
import socket
import time
from datetime import datetime
import csv
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QLabel, QTextEdit, QTableWidget, QTableWidgetItem, QLineEdit, \
    QPushButton, QMessageBox
from common_config_param import GlobalParam
from common_function import ip_format_check, welcome_init, clear_table_data, clear_log, log_format_print

Facility_Dict = {
    0: "[0]Kernel",
    1: "[1]User-Level",
    2: "[2]Mail",
    3: "[3]System",
    4: "[4]Security/Authorization-1",
    5: "[5]Syslogd",
    6: "[6]LinePrinter",
    7: "[7]NetworkNews",
    8: "[8]UUCP",
    9: "[9]ClockDaemon-1",
    10: "[10]Security/Authorization-2",
    11: "[11]FTP",
    12: "[12]NTP",
    13: "[13]LogAudit",
    14: "[14]LogAlert",
    15: "[15]ClockDaemon-2",
    16: "[16]LocalUse-0",
    17: "[17]LocalUse-1",
    18: "[18]LocalUse-2",
    19: "[19]LocalUse-3",
    20: "[20]LocalUse-4",
    21: "[21]LocalUse-5",
    22: "[22]LocalUse-6",
    23: "[23]LocalUse-7"

}


MessageLevel_Dict = {
    0: "[0]Emergency",
    1: "[1]Alert",
    2: "[2]Critical",
    3: "[3]Error",
    4: "[4]Warning",
    5: "[5]Notice",
    6: "[6]Info",
    7: "[7]Debug"

}


def save_table_widget(table_widget, file_path):
    with open(file_path, 'w', newline='') as file:
        writer = csv.writer(file, delimiter='\t')
        for row in range(table_widget.rowCount()):
            row_data = []
            for col in range(table_widget.columnCount()):
                item = table_widget.item(row, col)
                if item is not None:
                    row_data.append(item.text())
                else:
                    row_data.append('')
            writer.writerow(row_data)


class SysLogServerThread(QThread):
    _signal = pyqtSignal(dict)  # 定义带参数一个信号

    def __init__(self, ip):
        super().__init__()
        self.ip = ip
        self.stop_flag = False

    def run(self):  # 线程的执行方法
        res = {"code": "200", "message": "", "data": []}
        try:
            syslog_server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            address = (self.ip, 514)
            syslog_server_socket.bind(address)
            res["code"] = "201"
            res["message"] = "The IP address of the interface is successfully bound"
            self._signal.emit(res)
            self.stop_flag = False
        except:
            res["code"] = "404"
            res["message"] = "Failed to bind the IP address of the interface"
            self._signal.emit(res)
            self.stop_flag = False
            return
        syslog_server_socket.settimeout(1)
        time.sleep(1)
        res["code"] = "201"
        res["message"] = "The log server starts successfully"
        self._signal.emit(res)
        while not self.stop_flag:
            try:
                receive_data, client = syslog_server_socket.recvfrom(2048)
                rece_time = time.strftime(("%Y-%m-%d %H:%M:%S"), time.localtime())
                Priority = "".join(["%02X" % b for b in receive_data])
                start_id = Priority.find("C")
                end_id = Priority.find("3E")
                keys = Priority[start_id + 1:end_id]
                a = int(bytes.fromhex(keys))
                a_b = "{:08b}".format(a)
                facility = int(a_b[:5], 2)
                level = int(a_b[5:], 2)
                message = str(receive_data[end_id - 1:])[2:-1]
                res["code"] = "200"
                res["data"] = []
                res["data"].append(rece_time)
                res["data"].append(Facility_Dict[facility])
                res["data"].append(MessageLevel_Dict[level])
                res["data"].append(client[0])
                res["data"].append(message)
                self._signal.emit(res)
            except:
                """
                res["code"] = "200"
                res["data"] = []
                res["data"].append("2024-06-10 10:10:10")
                res["data"].append(Facility_Dict[4])
                res["data"].append(MessageLevel_Dict[4])
                res["data"].append("192.168.0.2")
                res["data"].append("ip_conntrack version 2.4 (4096 buckets, 32768 max) - 228 bytes per conntrack")
                self._signal.emit(res)
                """
                pass
        syslog_server_socket.close()
        time.sleep(1)
        res["code"] = "201"
        res["message"] = "The log server stop successfully"
        self._signal.emit(res)

    def stop(self):
        self.stop_flag = True


class SysLogServerLayout(QVBoxLayout):
    def __init__(self):
        super().__init__()

        self.running_flag = False

        self.ip = "127.0.0.1"

        self.thread_do = SysLogServerThread(self.ip)
        self.thread_do._signal.connect(self.update_message_or_data)

        top_layout = QHBoxLayout()

        server_ip_label = QLabel("SysLog Server IP:")
        self.server_ip_input = QLineEdit()
        self.server_ip_input.setText("127.0.0.1")

        self.start_button = QPushButton('Start')
        self.start_button.clicked.connect(self.start_test)
        self.start_button.setEnabled(True)

        self.stop_button = QPushButton('Stop')
        self.stop_button.clicked.connect(self.stop_test)
        self.stop_button.setEnabled(False)

        self.clear_log_button = QPushButton('Clear Log')
        self.clear_log_button.clicked.connect(lambda x: clear_log(self.log_area))

        self.export_button = QPushButton('Export Log Data')
        self.export_button.clicked.connect(self.export_log_data)

        self.clear_table_data_button = QPushButton('Clear Table')
        self.clear_table_data_button.clicked.connect(lambda x: clear_table_data(self.table))

        top_layout.addWidget(server_ip_label)
        top_layout.addWidget(self.server_ip_input)
        top_layout.addWidget(self.start_button)
        top_layout.addWidget(self.stop_button)
        top_layout.addWidget(self.clear_log_button)
        top_layout.addWidget(self.clear_table_data_button)
        top_layout.addWidget(self.export_button)

        self.addLayout(top_layout)

        self.table_layout = QHBoxLayout()
        self.table = QTableWidget()
        self.table.setAlternatingRowColors(True)
        self.table.setStyleSheet(
            "QTableWidget::item:selected { background-color: lightblue; } QTableWidget::item:alternate { "
            "background-color: lightgray; }")

        self.table.setColumnCount(5)

        self.table.setHorizontalHeaderLabels(["Data&Time", "Facility", "Level", "HostName", "Message"])

        self.table_layout.addWidget(self.table)

        self.addLayout(self.table_layout)

        log_label = QLabel("Real-time Logs")
        self.log_area = QTextEdit()
        self.log_area.setReadOnly(True)

        self.addWidget(log_label)
        self.addWidget(self.log_area)

        welcome_init(self.log_area)

    def export_log_data(self):
        if self.running_flag:
            QMessageBox.warning(self.parentWidget().parentWidget(),
                                "Attention",
                                "Running, stop and export!")
            return
        file_name_pre = str(datetime.now().strftime("%Y-%m-%d_%H%M%S_IP_")) + ".log"
        save_table_widget(self.table, file_name_pre)

    def update_message_or_data(self, res):
        if res["code"] == "201":
            log_format_print(self.log_area, res["message"])
        elif res["code"] == "404":
            log_format_print(self.log_area, res["message"])
            self.stop_test()
            pass
        elif res["code"] == "200":
            log_format_print(self.log_area, str(res["data"]))
            row_cnt = self.table.rowCount()
            self.table.insertRow(row_cnt)
            for i in range(5):
                self.table.setItem(row_cnt, i, QTableWidgetItem(str(res["data"][i])))
            self.table.scrollToBottom()

    def start_test(self):
        if GlobalParam.global_running_flag:
            QMessageBox.warning(self.parentWidget().parentWidget(), "WARNING",
                                "Other programs are running, please stop first")
            return
        ip_address = self.server_ip_input.text()
        if ip_format_check(ip_address):
            QMessageBox.warning(self.parentWidget().parentWidget(),
                                "Invalid IP",
                                "The entered IP address is invalid.")
            return

        GlobalParam.global_running_flag = True
        self.running_flag = True
        self.start_button.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.thread_do.ip = ip_address
        self.thread_do.stop_flag = False
        self.thread_do.start()

    def stop_test(self):
        self.thread_do.stop()
        time.sleep(1)
        while self.thread_do.isRunning():
            self.thread_do.stop()
        self.start_button.setEnabled(True)
        self.stop_button.setEnabled(False)
        self.running_flag = False
        GlobalParam.global_running_flag = False
